
const min_ = [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]
const max_ = [Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER]
class TeBox3 {
  min: number[]
  max: number[]
  size: number[]
  center: number[]

  centers: number[][]
  pointers: number[][]
  constructor(min: number[], max: number[]) {
    this.min = min
    this.max = max
    this.size = max.vSubNew(min)
    this.center = max.vAddNew(min).vScale(1 / 2)
    this.centers = []
    this.pointers = []
  }

  static new() {
    return new TeBox3(min_.vClone(), max_.vClone())
  }

  new() {
    return new TeBox3(min_.vClone(), max_.vClone())
  }


  setCenters(centers: number[][]) {
    this.centers = centers
    return this
  }

  setpointers(pointers: number[][]) {
    this.pointers = pointers
    return this
  }

  reSet() {
    let min = min_.vClone()
    let max = max_.vClone()
    this.min = min
    this.max = max
    this.size = max.vSubNew(min)
    this.center = max.vAddNew(min).vScale(1 / 2)
    this.centers = []
    this.pointers = []
    return this
  }

  union(box3: TeBox3) {
    this.min.vMin(box3.min)
    this.max.vMax(box3.max)

    this.size = this.max.vSubNew(this.min)
    this.center = this.max.vAddNew(this.min).vScale(1 / 2)
    return this
  }
}

class TeTool {

  static arrToBox3_a(points: Float32Array, indices: Uint32Array) {
    const box = TeBox3.new()
    const centers: number[][] = [], _pointers: number[][] = []
    for (let i = 0, l = indices.length; i < l; i += 4) {
      const j0 = indices[i + 1] * 3, j1 = indices[i + 2] * 3, j2 = indices[i + 3] * 3
      const a = [points[j0], points[j0 + 1], points[j0 + 2]]
      const b = [points[j1], points[j1 + 1], points[j1 + 2]]
      const c = [points[j2], points[j2 + 1], points[j2 + 2]]
      const d = a.vAddNew(b).vAdd(c).vScale(1 / 3)
      box.min.vMin(a).vMin(b).vMin(c)
      box.max.vMax(a).vMax(b).vMax(c)
      centers.push(d)
      _pointers.push(a, b, c)
    }
    box.center = box.max.vAddNew(box.min).vScale(1 / 2)
    box.size = box.max.vSubNew(box.min)
    return box.setCenters(centers).setpointers(_pointers)
  }

  static arrToBox3(points: number[][]) {
    const box = TeBox3.new()
    const centers: number[][] = []
    for (let i = 0, l = points.length; i < l; i += 3) {
      const a = points[i], b = points[i + 1], c = points[i + 2]
      const d = a.vAddNew(b).vAdd(c).vScale(1 / 3)
      box.min.vMin(a).vMin(b).vMin(c)
      box.max.vMax(a).vMax(b).vMax(c)
      centers.push(d)
    }
    box.center = box.max.vAddNew(box.min).vScale(1 / 2)
    box.size = box.max.vSubNew(box.min)
    return box.setCenters(centers)
  }

  static parseBinary(data: ArrayBuffer) {
    const reader = new DataView(data);
    const faces = reader.getUint32(80, true);

    const dataOffset = 84;
    const faceLength = 12 * 4 + 2;

    let offset = 0;

    const pointers = new Float32Array(faces * 3 * 3);

    for (var face = 0; face < faces; face++) {

      var start = dataOffset + face * faceLength;

      for (var i = 1; i <= 3; i++) {

        var vertexstart = start + i * 12;

        // ordering is intentional to match ascii import
        pointers[offset] = reader.getFloat32(vertexstart, true);
        pointers[offset + 2] = reader.getFloat32(vertexstart + 4, true);
        pointers[offset + 1] = reader.getFloat32(vertexstart + 8, true);


        offset += 3;
      }
    }
    return pointers
  }
}

class TeGeo {
  private _pointers: number[][]
  private _indices: number[]
  private _box3: TeBox3
  private _children: Map<number, TeGeo> = new Map<number, TeGeo>()
  private _parent?: TeGeo

  //数据
  private _points_: Float32Array = new Float32Array()
  private _indices_: Uint32Array = new Uint32Array()

  private _allChildren: TeGeo[] = []

  constructor(pointers: number[][], indices: number[], box3?: TeBox3) {
    this._pointers = pointers
    this._indices = indices
    this._box3 = box3 || TeTool.arrToBox3(pointers)
  }

  get pointers() {
    return this._pointers
  }
  get indices() {
    return this._indices
  }
  get box3() {
    return this._box3
  }

  get children() {
    return this._children
  }

  get parent() {
    return this._parent
  }

  get topParent() {
    let p: TeGeo = this
    while (p._parent) { p = p._parent }
    return p
  }

  get ptCount() {
    return this._indices.length
  }

  get allChildren() {
    return this._allChildren
  }

  get points_() {
    return this._points_
  }

  get indices_() {
    return this._indices_
  }

  addChild(egeo: TeGeo, index: number) {
    this._children.set(index, egeo)
    egeo._parent = this
  }

  setData(points: Float32Array, indices: Uint32Array) {
    this._points_ = points
    this._indices_ = indices
  }

  static parseData(points: Float32Array, indices: Uint32Array, ptMax = 5000) {
    const box3_p = TeTool.arrToBox3_a(points, indices)
    const egeo_p = new TeGeo(box3_p.pointers, indices.toArray_vtkPoly(), box3_p)
    egeo_p.setData(points, indices)
    parseChildren(egeo_p)
    return egeo_p

    function parseChildren(egeo: TeGeo) {
      const pst = egeo._pointers
      const box3 = egeo._box3, cts = box3.centers, center = box3.center, size = box3.size
      const addx = -center[0] + size[0] / 2
      const addy = -center[1] + size[1] / 2
      const addz = -center[2] + size[2] / 2
      const slen = size.vScaleNew(1 / 2)

      const geoDatas: { pointers: number[][], indices: number[] }[] = []
      for (let i = 0, l = cts.length; i < l; i++) {
        const ct = cts[i]
        const xInd = Math.floor((ct[0] + addx) / slen[0])
        const yInd = Math.floor((ct[1] + addy) / slen[1])
        const zInd = Math.floor((ct[2] + addz) / slen[2])
        const gInd = xInd * 2 * 2 + yInd * 2 + zInd
        let geoData = geoDatas[gInd]
        if (!geoData) {
          geoData = geoDatas[gInd] = { pointers: [], indices: [] }
        }
        const count = geoData.indices.length
        geoData.indices.push(count, count + 1, count + 2)
        geoData.pointers.push(pst[i * 3], pst[i * 3 + 1], pst[i * 3 + 2])
      }
      // //顶点分配不能为单数
      // let _geoData: any
      // for (const geoData of geoDatas) {
      //   if (_geoData)
      //   if (geoData.pointers.length % 2 !== 0) {

      //   }
      // }

      geoDatas.forEach((geoData, index) => {
        if (!geoData.indices.length) return
        const egeo = new TeGeo(geoData.pointers, geoData.indices)
        egeo.addChild(egeo, index)
        if (egeo.ptCount > ptMax) {
          parseChildren(egeo)
        } else {
          egeo.turnData()
          egeo_p.allChildren.push(egeo)
        }
      })
    }
  }

  //webgpu buffer 必须能整除4
  turnData() {
    const pts = this._pointers, pl = pts.length
    const isDan = pl % 2 === 1
    const pal = isDan ? (pl + 1) * 3 : pl * 3
    const points = new Float32Array(pal)
    for (let i = 0; i < pl; i++) {
      const j = i * 3, pt = pts[i]
      points[j] = pt[0]
      points[j + 1] = pt[1]
      points[j + 2] = pt[2]
    }
    if (isDan) {
      const lpts = points[pl - 1], pal_ = pl * 3
      points[pal_] = lpts[0]
      points[pal_ + 1] = lpts[1]
      points[pal_ + 2] = lpts[2]
    }
    this._points_ = points
    const ids = this._indices, il = ids.length
    const pil = isDan ? (il / 3 + 1) * 4 : il / 3 * 4
    const indices = new Uint32Array(pil)
    let index = 0
    for (let i = 0; i < il; i += 3) {
      indices[index++] = 3
      indices[index++] = ids[i]
      indices[index++] = ids[i + 1]
      indices[index++] = ids[i + 2]
    }
    if (isDan) {
      indices[index++] = 3
      indices[index++] = ids[il - 3]
      indices[index++] = ids[il - 2]
      indices[index++] = ids[il - 1]
    }
    this._indices_ = indices
    console.log(indices, points)
  }

}
export { TeTool, TeGeo }